package com.csg.ses.common.utils;

import com.csg.ses.common.configuration.SftpProperties;
import com.jcraft.jsch.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;

/**
 * sftp工具类
 * @auther wangjun
 * @time 2020/11/16
 */
@Service
public class SftpFileUtil {

    @Autowired
    private SftpProperties sftpProperty;

    /**
     * 创建sftp连接
     * @return
     * @throws Exception
     */
    public ChannelSftp connect() throws Exception {
        JSch jsch = new JSch();

        Session session = this.createSession(jsch);
        session.setPassword(sftpProperty.getPassword());
        session.connect(sftpProperty.getSessionConnectTimeout());

        Channel channel = session.openChannel(sftpProperty.getProtocol());
        channel.connect(sftpProperty.getChannelConnectedTimeout());
        return (ChannelSftp) channel;
    }

    /**
     * 创建session
     * @param jsch
     * @return
     * @throws Exception
     */
    private Session createSession(JSch jsch) throws Exception {
        Session session = null;
        if (sftpProperty.getPort() <= 0) {
            session = jsch.getSession(sftpProperty.getUsername(), sftpProperty.getHost());
        } else {
            session = jsch.getSession(sftpProperty.getUsername(), sftpProperty.getHost(), sftpProperty.getPort());
        }
        if (session == null) {
            throw new RuntimeException(sftpProperty.getHost() + " session is null");
        }
        session.setConfig("StrictHostKeyChecking", sftpProperty.getSessionStrictHostKeyChecking());
        return session;
    }

    /**
     * 加密秘钥方式登陆
     * @return
     */
    private ChannelSftp connectByKey() throws Exception {
        JSch jsch = new JSch();

        //设置密钥和密码 ,支持密钥的方式登陆
        if (StringUtils.isNotBlank(sftpProperty.getPrivateKey())) {
            if (StringUtils.isNotBlank(sftpProperty.getPassphrase())) {
                jsch.addIdentity(sftpProperty.getPrivateKey(), sftpProperty.getPassphrase());   //设置带口令的密钥
            } else {
                jsch.addIdentity(sftpProperty.getPrivateKey()); //设置不带口令的密钥
            }
        }

        Session session = this.createSession(jsch);
        session.connect(sftpProperty.getSessionConnectTimeout());   //设置登录超时时间
        Channel channel = session.openChannel(sftpProperty.getProtocol());  //创建sftp通信通道
        channel.connect(sftpProperty.getChannelConnectedTimeout());
        return (ChannelSftp) channel;
    }

    /**
     * 关闭连接
     * @param sftp
     */
    private void disconnect(ChannelSftp sftp) {
        try {
            if (sftp != null) {
                if (sftp.isConnected()) {
                    sftp.disconnect();
                }
                if (sftp.getSession() != null) {
                    sftp.getSession().disconnect();
                }
            }
        } catch (JSchException e) {
            e.printStackTrace();
        }
    }

    /**
     * 多级目录创建
     * @param dirPath
     * @param sftp
     * @return
     */
    private boolean createDirs(String dirPath, ChannelSftp sftp) {
        if (dirPath != null && !dirPath.isEmpty() && sftp != null) {
            String[] dirs = Arrays.stream(dirPath.split("/"))
                    .filter(StringUtils::isNotBlank)
                    .toArray(String[]::new);
            for (String dir : dirs) {
                try {
                    sftp.cd(dir);
                } catch (Exception e) {
                    try {
                        sftp.mkdir(dir);
                    } catch (SftpException e1) {
                        e1.printStackTrace();
                    }
                    try {
                        sftp.cd(dir);
                    } catch (SftpException e1) {
                        e1.printStackTrace();
                    }
                }
            }
            return true;
        }
        return false;
    }

    /**
     * 上传inputStream到指定目录
     * @param targetPath
     * @param inputStream
     * @return
     * @throws Exception
     */
    public boolean uploadFile(String targetPath, InputStream inputStream) throws Exception {
        ChannelSftp sftp = this.connect();
        try {
//            sftp.cd(sftpProperty.getRootpath());
            int index = targetPath.lastIndexOf("/");
            String fileDir = targetPath.substring(0, index);
            String fileName = targetPath.substring(index + 1);
            boolean dirs = this.createDirs(fileDir, sftp);
            if (!dirs) {
                throw new RuntimeException("指定目录创建失败！");
            }
            sftp.put(inputStream, fileName);
            return true;
        } catch (Exception e) {
            throw new RuntimeException("文件上传失败");
        } finally {
            this.disconnect(sftp);
        }
    }

    /**
     * 文件下载
     * @param targetPath
     * @return
     * @throws Exception
     */
    public File downloadFile(String targetPath) throws Exception {
        ChannelSftp sftp = this.connect();
        OutputStream outputStream = null;
        try {
            sftp.cd(sftpProperty.getRootpath());
            String filename = targetPath.substring(targetPath.lastIndexOf("/") + 1);
            File file = new File(sftpProperty.getTmppath() + filename);
            if (!file.getParentFile().exists()) {   //临时目录若不存在则新建
                file.getParentFile().mkdirs();
            }
            if (file.exists()) {    //文件若存在则删除重新创建
                file.delete();
            }
            file.createNewFile();
            outputStream = new FileOutputStream(file);
            sftp.get(targetPath, outputStream);
            return file;
        } catch (Exception e) {
            throw new RuntimeException();
        } finally {
            if (outputStream != null) {
                outputStream.close();
            }
            this.disconnect(sftp);
        }
    }

    /**
     * 文件删除
     * @param targetPath
     * @return
     * @throws Exception
     */
    public boolean deleteFile(String targetPath) throws Exception {
        ChannelSftp sftp = null;
        try {
            sftp = this.connect();
            sftp.cd(sftpProperty.getRootpath());
            sftp.rm(targetPath);
            return true;
        } catch (Exception e) {
            throw new RuntimeException("文件删除失败！");
        } finally {
            this.disconnect(sftp);
        }
    }
}